#pragma once
#include "Util.hpp"
#include <mutex>
#include <cassert>

class user_table
{
public:
    user_table(const std::string &host,
               const std::string &username,
               const std::string &password,
               const std::string &dbname,
               uint16_t port = 33360)
        : _stop_keepalive(false)
    {
        _mysql = mysql_util::mysql_create(host, username, password, dbname, port);
        assert(_mysql != NULL);
        ILOG("user_table: mysql init success!");

        // 启动保活线程
        _keepalive_thread = std::thread([this]()
                                        {
            while (true) {
                std::unique_lock<std::mutex> lock(_mutex);
                // 等待5小时或直到收到停止信号
                if (_cv_keepalive.wait_for(lock, std::chrono::hours(3), [this] { 
                    return _stop_keepalive.load(); 
                })) {
                    break; // 收到停止信号，退出线程
                }

                // 执行保活查询
                bool ret = mysql_util::mysql_exec(_mysql, "SELECT 1");
             if (ret) 
             {
                MYSQL_RES* result = mysql_store_result(_mysql);
                if (result != nullptr) 
                {
                    mysql_free_result(result); //释放结果集
                } 
                else 
                {
            // 处理无结果集的情况（如查询失败）
            ELOG("Keepalive failed: %s", mysql_error(_mysql));
                }}
            }});
    }
    ~user_table()
    {
        // 通知保活线程停止
        _stop_keepalive = true;
        _cv_keepalive.notify_one();

        // 等待保活线程结束
        if (_keepalive_thread.joinable())
        {
            _keepalive_thread.join();
        }

        mysql_util::mysql_destroy(_mysql);
        _mysql = NULL;
    }

    // 注册时新增用户
    void insert(Json::Value const &request, Json::Value &response)
    {
        // need params: password, username
#define INSERT_USER "insert into user(username, password, score, total_count, win_count) values('%s', SHA2('%s', 256), 1000, 0, 0);"
        if (request["password"].isNull() || request["username"].isNull())
        {
            DLOG("user_insert: request no password or username");
            response["result"] = false;
            response["reason"] = "no params password or username";
            return;
        }
        char sql[4096] = {0};
        sprintf(sql, INSERT_USER, request["username"].asCString(), request["password"].asCString());
        bool ret = mysql_util::mysql_exec(_mysql, sql);
        if (ret == false)
        {
            ELOG("insert user info failed!\n");
            response["result"] = false;
            response["reason"] = "user_insert: mysql_exec failed!";
            return;
        }
        DLOG("user_table: insert user:%s success", request["username"].asCString());
        response["result"] = true;
        return;
    }

    // 登录验证，返回用户信息到user中
    void login(Json::Value const &request, Json::Value &response)
    {
        // need params: password, username
        if (request["password"].isNull() || request["username"].isNull())
        {
            DLOG("login: request no password or username");
            response["result"] = false;
            response["reason"] = "no params password or username";
            return;
        }
        // 以用户名和密码共同作为查询过滤条件，查询到数据则表示用户名密码一致，没有信息则用户名密码错误
#define LOGIN_USER "select id, score, total_count, win_count from user where username='%s' and password=SHA2('%s', 256);"
        char sql[4096] = {0};
        sprintf(sql, LOGIN_USER, request["username"].asCString(), request["password"].asCString());
        MYSQL_RES *res = NULL;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret = mysql_util::mysql_exec(_mysql, sql);
            if (ret == false)
            {
                ELOG("user login failed!");
                response["result"] = false;
                response["reason"] = "user_login: mysql_exec failed!";
                return;
            }
            res = mysql_store_result(_mysql);
        }
        int row_num = mysql_num_rows(res);
        if (row_num == 0)
        {
            ILOG("login failed! username:%s or password:%s error!", request["username"].asCString(), request["password"].asCString());
            mysql_free_result(res);
            response["result"] = false;
            response["reason"] = "username or password error";
            return;
        }
        if (row_num > 1) // 如果有数据，必须唯一
        {
            ELOG("the user information queried is not unique! row_num:%d", row_num);
            for (size_t i = 0; i <= row_num; i++)
            {
                MYSQL_ROW row = mysql_fetch_row(res);
                ELOG("not unique: %s, %s, %s, %s", row[0], row[1], row[2], row[3]);
            }
            response["result"] = false;
            response["reason"] = "data not unique!";
            return;
        }
        MYSQL_ROW row = mysql_fetch_row(res);
        response["result"] = true;
        response["id"] = (Json::UInt64)std::stol(row[0]);
        response["score"] = (Json::UInt64)std::stol(row[1]);
        response["total_count"] = std::stoi(row[2]);
        response["win_count"] = std::stoi(row[3]);
        mysql_free_result(res);
        return;
    }

    // 通过用户名获取用户信息
    void select_by_name(Json::Value const &request, Json::Value &response)
    {
        // need params: username
        if (request["username"].empty())
        {
            DLOG("select_by_name: request no username")
            response["result"] = false;
            response["reason"] = "no params username";
            return;
        }
#define SELECT_BY_NAME "select id, score, total_count, win_count from user where username='%s';"
        char sql[4096] = {0};
        sprintf(sql, SELECT_BY_NAME, request["name"].asCString());
        MYSQL_RES *res = NULL;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret = mysql_util::mysql_exec(_mysql, sql);
            if (ret == false)
            {
                ELOG("select by name failed!\n");
                response["result"] = false;
                response["reason"] = "select_by_name: mysql_exec failed!";
                return;
            }
            res = mysql_store_result(_mysql);
        }
        int row_num = mysql_num_rows(res);
        if (row_num == 0)
        {
            ILOG("username:%s have no user info!", request["name"].asCString());
            response["result"] = false;
            response["reason"] = "username have no info!";
            return;
        }
        if (row_num > 1) // 如果有数据，必须唯一
        {
            ELOG("the user information queried is not unique!");
            for (size_t i = 0; i < row_num; i++)
            {
                MYSQL_ROW row = mysql_fetch_row(res);
                ELOG("not unique: id:%s", row[0]);
            }
            response["result"] = false;
            response["reason"] = "data not unique!";
            return;
        }
        MYSQL_ROW row = mysql_fetch_row(res);
        response["result"] = true;
        response["id"] = (Json::UInt64)std::stol(row[0]);
        response["username"] = request["username"];
        response["score"] = (Json::UInt64)std::stol(row[1]);
        response["total_count"] = std::stoi(row[2]);
        response["win_count"] = std::stoi(row[3]);
        mysql_free_result(res);
        return;
    }

    // 通过id获取用户信息
    void select_by_id(Json::Value const &request, Json::Value &response)
    {
        // need params: uid
        if (request["uid"].empty())
        {
            DLOG("select_by_id: request no uid");
            response["result"] = false;
            response["reason"] = "no params uid";
            return;
        }
#define SELECT_BY_ID "select username, score, total_count, win_count from user where id=%ld;"
        char sql[4096] = {0};
        sprintf(sql, SELECT_BY_ID, request["uid"].asUInt64());
        MYSQL_RES *res = NULL;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret = mysql_util::mysql_exec(_mysql, sql);
            if (ret == false)
            {
                ELOG("select by id failed!\n");
                response["result"] = false;
                response["reason"] = "select_by_id: mysql_exec failed!";
                return;
            }
            res = mysql_store_result(_mysql);
        }

        int row_num = mysql_num_rows(res);
        if (row_num == 0)
        {
            DLOG("id:%ld have no user info!", request["id"].asUInt64());
            response["result"] = false;
            response["reason"] = "uid have no info!";
            return;
        }
        if (row_num > 1) // 如果有数据，必须唯一
        {
            ELOG("the user information queried is not unique!");
            for (size_t i = 0; i < row_num; i++)
            {
                MYSQL_ROW row = mysql_fetch_row(res);
                ELOG("not unique: username:%s", row[0]);
            }
            response["result"] = false;
            response["reason"] = "data not unique";
            return;
        }
        MYSQL_ROW row = mysql_fetch_row(res);
        response["result"] = true;
        response["id"] = (Json::UInt64)request["uid"].asUInt64();
        response["username"] = row[0];
        response["score"] = (Json::UInt64)std::stol(row[1]);
        response["total_count"] = std::stoi(row[2]);
        response["win_count"] = std::stoi(row[3]);
        mysql_free_result(res);
        return;
    }

    // 胜利时增加50积分，战斗场次增加1，胜利场次增加1
    void win(Json::Value const &request, Json::Value &response)
    {
        // need params: uid
        if (request["uid"].empty())
        {
            DLOG("win: request no uid");
            response["result"] = false;
            response["reason"] = "no params uid";
            return;
        }
#define USER_WIN "update user set score=score+50, total_count=total_count+1, win_count=win_count+1 where id=%ld;"
        char sql[4096] = {0};
        sprintf(sql, USER_WIN, request["uid"].asUInt64());
        bool ret = mysql_util::mysql_exec(_mysql, sql);
        if (ret == false)
        {
            ELOG("user_table update win failed! id:%ld", request["uid"].asUInt64());
            response["result"] = false;
            response["reason"] = "win: mysql_exec failed!";
            return;
        }
        response["result"] = true;
        return;
    }

    // 失败时减少50积分，战斗场次增加1，其他不变
    void lose(Json::Value const &request, Json::Value &response)
    {
        // need params: uid
        if (request["uid"].empty())
        {
            DLOG("win: request no uid");
            response["result"] = false;
            response["reason"] = "no params uid";
            return;
        }
#define USER_LOSE "update user set score=score-50, total_count=total_count+1 where id=%ld;"
        char sql[4096] = {0};
        sprintf(sql, USER_LOSE, request["uid"].asUInt64());
        bool ret = mysql_util::mysql_exec(_mysql, sql);
        if (ret == false)
        {
            ELOG("user_table update lose failed! id:%ld", request["uid"].asUInt64());
            response["result"] = false;
            response["reason"] = "lose: mysql_exec failed!";
            return;
        }
        response["result"] = true;
        return;
    }

private:
    MYSQL *_mysql;     // mysql操作句柄
    std::mutex _mutex; // 互斥锁保护数据库的访问操作

    std::thread _keepalive_thread;         // 保活线程
    std::condition_variable _cv_keepalive; // 保活线程条件变量
    std::atomic<bool> _stop_keepalive;     // 保活线程停止标志
};
